#include "outline/poly_bezier_builder_base.h"
#include "outline/bezier_guide.h"
#include "outline/bspline_opt.h"
#include "outline/segmap_impl.h"

namespace outline {

template <typename T, typename R>
poly_bezier_builder_base<T, R>::poly_bezier_builder_base()
{
	size_ = 0;
}

template <typename T, typename R>
poly_bezier_builder_base<T, R>::~poly_bezier_builder_base()
{
}

template <typename T, typename R>
void poly_bezier_builder_base<T, R>::reset_nodes(nodes_type const & polyline)
{
	if (polyline.empty())
		return clear_nodes();
	assert(closed(polyline));
	size_ = polyline.size() - 1;
	nodes_.reserve(size_*2);
	nodes_.assign(polyline.begin(), polyline.end()-1);
	nodes_.insert(nodes_.end(), polyline.begin(), polyline.end()-1);
	assert(nodes_.size() == size_*2);
}

template <typename T, typename R>
void poly_bezier_builder_base<T, R>::clear_nodes()
{
	size_ = 0;
	nodes_.clear();
}

template <typename T, typename R>
R poly_bezier_builder_base<T, R>::collect_best(
	controls<R> * rendered_ctrls, controls<R> * estimated_ctrls,
	real_point_vector_type const * attractors, real_vector_type const * tractions)
{
	poly_bezier_builder_params<R> const & p = get_params();
	if (rendered_ctrls)
		rendered_ctrls->clear();
	if (estimated_ctrls)
		estimated_ctrls->clear();
	index_vector_type ivec;
	R r = select_best(ivec);
	if (r < 0)
		return r;
	assert (ivec.size() >= 2);
	uint csize = (ivec.size()-1)*3 + 1;

	if (p.low && estimated_ctrls) {
		estimated_ctrls->reserve(csize);
		estimated_ctrls->push_back(nodes_[ivec[0]]);
		for (uint i = 0; i < ivec.size()-1; ++i) {
			uint start = ivec[i];
			uint finish = ivec[i+1];
			assert (finish > start);
			assert (estimated_ctrls->back() == nodes_[start]);
			estimation_type const & e = get_estimation(start % size_, finish - start + 1, i);
			estimated_ctrls->push_back(real_point_type(nodes_[start]) + e.d1);
			estimated_ctrls->push_back(real_point_type(nodes_[finish]) + e.d2);
			estimated_ctrls->push_back(nodes_[finish]);
		}
	}

	if (rendered_ctrls) {
		controls<R> & ctrls = *rendered_ctrls;
		ctrls.reserve(csize);
		real_point_vector_type poles;
		bpoint_vector bvec;

		ctrls.push_back(nodes_[ivec[0]]);
		for (uint i = 0; i < ivec.size()-1; ++i) {
			uint start = ivec[i];
			uint finish = ivec[i+1];
			assert (finish > start);
			assert (ctrls.back() == nodes_[start]);

			bezier_type b;
			real_point_vector_type subpoles;
			bpoint_vector subbvec;
			bezier_guide(
					nodes_, start, finish + 1,
					p.hi_max_iterations,
					p.hi_stop_mean_discrepancy,
					p.hi_oversample_factor,
					false, false,
					b,
					subpoles,
					subbvec
				);
			assert(nodes_[start] == b.start());
			assert(nodes_[finish] == b.finish());
			ctrls.push_back(b);
			if (p.fit_poly_bezier_as_a_whole) {
				poles.insert(poles.end(), subpoles.begin(), subpoles.end()-1);
				bvec.insert(bvec.end(), subbvec.begin(), subbvec.end()-1);
			}
		}

		if (p.fit_poly_bezier_as_a_whole) {
			std::vector<junc_type> junc(ivec.size()-1);
			get_junction_types(ivec, junc);
			bspline_opt<T, R>::doit(
				segmap<T>(nodes_.begin(), nodes_.begin() + size_ + 1),
				junc,
				bspline_opt_params(
					p.hi_max_iterations,
					1 / p.hi_oversample_factor,
					fit_local,
					translation, //not really used
					false //no antiloop
				),
				ctrls,
				attractors,
				tractions
			);
		}
	}

	return r;
}

template poly_bezier_builder_base<unsigned short>;
template poly_bezier_builder_base<unsigned short, double>;
template poly_bezier_builder_base<short>;
template poly_bezier_builder_base<int>;
template poly_bezier_builder_base<float>;
template poly_bezier_builder_base<double>;

} //namespace outline
